using FuzzScript;

using System;



public class Parser {
	public const int _EOF = 0;
	public const int _number = 1;
	public const int _ident = 2;
	public const int _HEDGE_VERY = 3;
	public const int _HEDGE_NOT = 4;
	public const int _OP_AND = 5;
	public const int _OP_OR = 6;
	public const int _OPEN_CURLY = 7;
	public const int _CLOSE_CURLY = 8;
	public const int _OPEN_SQUARE = 9;
	public const int _CLOSE_SQUARE = 10;
	public const int _OPEN_BRACE = 11;
	public const int _CLOSE_BRACE = 12;
	public const int _PARAM_DELIM = 13;
	public const int _RULE_DELIM = 14;
	public const int _CMD_IF = 15;
	public const int _CMD_THEN = 16;
	public const int _CMD_IS = 17;
	public const int _CMD_RULEBASE = 18;
	public const int _CMD_PREDICATES = 19;
	public const int _CMD_PREDICATE = 20;
	public const int _CMD_INPUTVARIABLE = 21;
	public const int _CMD_OUTPUTVARIABLE = 22;
	public const int _CMD_KNOWLEDGEBASE = 23;
	public const int _CMD_SETTRIANGULAR = 24;
	public const int _CMD_SETLSHOULDER = 25;
	public const int _CMD_SETRSHOULDER = 26;
	public const int maxT = 30;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;
	
	public Scanner scanner;
	public Errors  errors;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;

internal FuzzySystem m_System = new FuzzySystem();



	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public void SemErr (string msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}


	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	
	void FuzzScript() {
		INode rulebase, knowledgebase, predicates;
		KnowledgeBase(out knowledgebase);
		m_System.KnowledgeBase = knowledgebase;
		Predicates(out predicates);
		m_System.Predicates = predicates;
		RuleBase(out rulebase);
		m_System.RuleBase = rulebase;
	}

	void KnowledgeBase(out INode p_CurrentNode) {
		INode variables = null;
		Expect(23);
		KnowledgeBaseNode knowledgeBase = new KnowledgeBaseNode();
		Expect(7);
		if (la.kind == 21) {
			InputVariables(out variables);
		}
		knowledgeBase.InputVariables = variables;
		if (la.kind == 22) {
			OutputVariables(out variables);
		}
		knowledgeBase.OutputVariables = variables;
		Expect(8);
		p_CurrentNode = knowledgeBase;
	}

	void Predicates(out INode  p_CurrentNode) {
		INode predicate = null;
		PredicatesNode predicates = new PredicatesNode();
		Expect(19);
		Expect(7);
		if (la.kind == 20) {
			Predicate(out predicate);
			predicates.AddSibling(predicate);
			while (la.kind == 20) {
				Predicate(out predicate);
				predicates.AddSibling(predicate);
			}
		}
		p_CurrentNode = predicates;
		Expect(8);
	}

	void RuleBase(out INode p_CurrentNode) {
		INode rules = null;
		Expect(18);
		Expect(7);
		Rules(out rules);
		Expect(8);
		p_CurrentNode  = rules;
	}

	void InputVariables(out INode p_CurrentNode) {
		INode input = null;
		INode inputVariables = new Node();
		InputVariable(out input);
		inputVariables.AddSibling(input);
		while (la.kind == 21) {
			InputVariable(out input);
			inputVariables.AddSibling(input);
		}
		p_CurrentNode = inputVariables; 
	}

	void OutputVariables(out INode p_CurrentNode) {
		INode output = null;
		INode outputVariables = new Node();
		OutputVariable(out output);
		outputVariables.AddSibling(output);
		while (la.kind == 22) {
			OutputVariable(out output);
			outputVariables.AddSibling(output);
		}
		p_CurrentNode = outputVariables;
	}

	void InputVariable(out INode p_CurrentNode) {
		FuzzyVariable var = null;
		Expect(21);
		double dLowLim, dUpLim;
		Expect(2);
		string sName = t.val;
		Expect(9);
		Expect(1);
		dLowLim = Convert.ToDouble(t.val);
		Expect(13);
		Expect(1);
		dUpLim = Convert.ToDouble(t.val);
		Expect(10);
		var = new FuzzyVariable(sName, dLowLim, dUpLim, true);
		Expect(7);
		FuzzySets(var);
		Expect(8);
		p_CurrentNode = var;
	}

	void FuzzySets(FuzzyVariable p_FuzzyVariable) {
		INode set = null; 
		FuzzySet(out set, p_FuzzyVariable);
		p_FuzzyVariable.AddSibling(set);
		while (la.kind == 24 || la.kind == 25 || la.kind == 26) {
			FuzzySet(out set, p_FuzzyVariable);
			p_FuzzyVariable.AddSibling(set);
		}
	}

	void OutputVariable(out INode p_CurrentNode) {
		FuzzyVariable var = null;
		Expect(22);
		double dLowLim, dUpLim;
		Expect(2);
		string sName = t.val;
		Expect(9);
		Expect(1);
		dLowLim = Convert.ToDouble(t.val);
		Expect(13);
		Expect(1);
		dUpLim = Convert.ToDouble(t.val);
		Expect(10);
		var = new FuzzyVariable(sName, dLowLim, dUpLim, false);
		Expect(7);
		FuzzySets(var);
		Expect(8);
		p_CurrentNode = var;
	}

	void FuzzySet(out INode p_CurrentNode, 
FuzzyVariable p_FuzzyVariable) {
		INode set = null;
		if (la.kind == 24) {
			TriangularSet(out set, p_FuzzyVariable);
		} else if (la.kind == 25) {
			LShoulderSet(out set, p_FuzzyVariable);
		} else if (la.kind == 26) {
			RShoulderSet(out set, p_FuzzyVariable);
		} else SynErr(31);
		p_CurrentNode=set;
	}

	void TriangularSet(out INode p_CurrentNode, 
FuzzyVariable p_FuzzyVariable) {
		string name; 
		int p1, p2,p3;	
		Expect(24);
		Expect(11);
		Expect(2);
		name = t.val;
		Expect(13);
		Expect(1);
		p1 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(13);
		Expect(1);
		p2 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(13);
		Expect(1);
		p3 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(12);
		p_CurrentNode = new TriangularFuzzySet
		(name,p1,p2,p3);
	}

	void LShoulderSet(out INode p_CurrentNode, FuzzyVariable p_FuzzyVariable) {
		string name; 
		int p1, p2;	
		Expect(25);
		Expect(11);
		Expect(2);
		name = t.val;
		Expect(13);
		Expect(1);
		p1 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(13);
		Expect(1);
		p2 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(12);
		p_CurrentNode = new LShoulderFuzzySet
		(name,p1,p2);
	}

	void RShoulderSet(out INode p_CurrentNode, FuzzyVariable p_FuzzyVariable) {
		string name; 
		int p1, p2;
		Expect(26);
		Expect(11);
		Expect(2);
		name = t.val;
		Expect(13);
		Expect(1);
		p1 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(13);
		Expect(1);
		p2 = p_FuzzyVariable.VariableToDiscreteIndex( Convert.ToDouble(t.val));
		Expect(12);
		p_CurrentNode = new RShoulderFuzzySet
		(name,p1,p2);
	}

	void Predicate(out INode p_PredicateNode) {
		INode expressionNode = null;
		Expect(20);
		Expect(2);
		FuzzyPredicate pred = new FuzzyPredicate(t.val);
		Expect(7);
		Expression(out expressionNode);
		Expect(8);
		pred.Expression = expressionNode;
		p_PredicateNode = pred;
	}

	void Expression(out INode p_CurrentNode) {
		INode currentNode = null;
		Term(out currentNode);
		while (la.kind == 5 || la.kind == 6) {
			FuzzyOperator currentOperator = null;
			INode rNode = null;
			Operator(out currentOperator);
			Term(out rNode);
			currentOperator.LeftNode = currentNode;
			currentOperator.RightNode = rNode;
			currentNode = currentOperator;
		}
		p_CurrentNode = currentNode;
	}

	void Rules(out INode p_CurrentNode) {
		Node rules = new Node();
		INode rule = null;
		Rule(out rule);
		rules.AddSibling(rule);
		while (la.kind == 15) {
			Rule(out rule);
			rules.AddSibling(rule);
		}
		p_CurrentNode=rules;
	}

	void Rule(out INode p_CurrentNode) {
		INode antecedent = null;
		FuzzyRule rule = null;
		Expect(15);
		AntecedentExpression(out antecedent);
		Expect(16);
		ConsequentExpression(out rule);
		rule.AntecedentExpression = antecedent;
		p_CurrentNode=rule;
	}

	void AntecedentExpression(out INode p_CurrentNode) {
		INode expression = null;
		Expression(out expression);
		p_CurrentNode = expression;
	}

	void ConsequentExpression(out FuzzyRule p_CurrentNode) {
		FuzzyRule rule = new FuzzyRule();
		INode clause = null;
		ConsequentClause(out clause);
		rule.AddSibling(clause);
		while (la.kind == 5) {
			Get();
			ConsequentClause(out clause);
			rule.AddSibling(clause);
		}
		p_CurrentNode = rule;
	}

	void ConsequentClause(out INode p_CurrentNode) {
		INode node = null;
		Expect(2);
		string sVariableName = t.val;
		if (la.kind == 17) {
			Get();
			Expect(2);
			string sSetName = t.val;
			node = new ConsequentSetClause(sVariableName,sSetName);
		} else if (la.kind == 27) {
			Get();
			Expect(1);
			node = new ConsequentAssignmentClause(sVariableName,Convert.ToDouble(t.val));
		} else SynErr(32);
		p_CurrentNode = node;
	}

	void Term(out INode p_CurrentNode) {
		INode currentNode = null;
		if (la.kind == 11) {
			Get();
			Expression(out currentNode);
			Expect(12);
		} else if (la.kind == 3 || la.kind == 4) {
			INode hedge, hedgedNode;
			FuzzyHedge(out hedge);
			Term(out hedgedNode);
			hedge.AddSibling(hedgedNode);
			currentNode=hedge;
		} else if (la.kind == 2 || la.kind == 28) {
			Clause(out currentNode);
		} else SynErr(33);
		p_CurrentNode = currentNode;
	}

	void Operator(out FuzzyOperator p_CurrentNode) {
		FuzzyOperator fuzzyOperator = null;
		if (la.kind == 5) {
			Get();
			fuzzyOperator = new OperatorAnd();
		} else if (la.kind == 6) {
			Get();
			fuzzyOperator = new OperatorOr();
		} else SynErr(34);
		p_CurrentNode = fuzzyOperator;
	}

	void FuzzyHedge(out INode p_CurrentNode) {
		INode fuzzyHedge = null;
		if (la.kind == 3) {
			Get();
			fuzzyHedge = new HedgeVery();
		} else if (la.kind == 4) {
			Get();
			fuzzyHedge = new HedgeNot();
		} else SynErr(35);
		p_CurrentNode = fuzzyHedge;
	}

	void Clause(out INode p_CurrentNode) {
		INode currentNode = null;
		if (la.kind == 28) {
			PredicateClause(out currentNode);
		} else if (la.kind == 2) {
			AntecedentSetClause(out currentNode);
		} else SynErr(36);
		p_CurrentNode = currentNode;
	}

	void PredicateClause(out INode p_CurrentNode) {
		Expect(28);
		Expect(2);
		PredicateClause  pedicate= new PredicateClause(t.val);
		Expect(29);
		p_CurrentNode = pedicate;
	}

	void AntecedentSetClause(out INode p_CurrentNode) {
		string sVariableName, sSetName;
		Expect(2);
		sVariableName = t.val;
		Expect(17);
		Expect(2);
		sSetName = t.val;
		AntecedentSetClause clause = new AntecedentSetClause(sVariableName,sSetName);
		p_CurrentNode = clause;
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		FuzzScript();

    Expect(0);
	}
	
	static readonly bool[,] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}

	};
} // end Parser


public class Errors {
	public int count = 0;                                    // number of errors detected
	public System.IO.TextWriter errorStream = Console.Out;   // error messages go to this stream
  public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text
  
	public void SynErr (int line, int col, int n) {
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "number expected"; break;
			case 2: s = "ident expected"; break;
			case 3: s = "HEDGE_VERY expected"; break;
			case 4: s = "HEDGE_NOT expected"; break;
			case 5: s = "OP_AND expected"; break;
			case 6: s = "OP_OR expected"; break;
			case 7: s = "OPEN_CURLY expected"; break;
			case 8: s = "CLOSE_CURLY expected"; break;
			case 9: s = "OPEN_SQUARE expected"; break;
			case 10: s = "CLOSE_SQUARE expected"; break;
			case 11: s = "OPEN_BRACE expected"; break;
			case 12: s = "CLOSE_BRACE expected"; break;
			case 13: s = "PARAM_DELIM expected"; break;
			case 14: s = "RULE_DELIM expected"; break;
			case 15: s = "CMD_IF expected"; break;
			case 16: s = "CMD_THEN expected"; break;
			case 17: s = "CMD_IS expected"; break;
			case 18: s = "CMD_RULEBASE expected"; break;
			case 19: s = "CMD_PREDICATES expected"; break;
			case 20: s = "CMD_PREDICATE expected"; break;
			case 21: s = "CMD_INPUTVARIABLE expected"; break;
			case 22: s = "CMD_OUTPUTVARIABLE expected"; break;
			case 23: s = "CMD_KNOWLEDGEBASE expected"; break;
			case 24: s = "CMD_SETTRIANGULAR expected"; break;
			case 25: s = "CMD_SETLSHOULDER expected"; break;
			case 26: s = "CMD_SETRSHOULDER expected"; break;
			case 27: s = "\"=\" expected"; break;
			case 28: s = "\"<\" expected"; break;
			case 29: s = "\">\" expected"; break;
			case 30: s = "??? expected"; break;
			case 31: s = "invalid FuzzySet"; break;
			case 32: s = "invalid ConsequentClause"; break;
			case 33: s = "invalid Term"; break;
			case 34: s = "invalid Operator"; break;
			case 35: s = "invalid FuzzyHedge"; break;
			case 36: s = "invalid Clause"; break;

			default: s = "error " + n; break;
		}
		errorStream.WriteLine(errMsgFormat, line, col, s);
		count++;
	}

	public void SemErr (int line, int col, string s) {
		errorStream.WriteLine(errMsgFormat, line, col, s);
		count++;
	}
	
	public void SemErr (string s) {
		errorStream.WriteLine(s);
		count++;
	}
	
	public void Warning (int line, int col, string s) {
		errorStream.WriteLine(errMsgFormat, line, col, s);
	}
	
	public void Warning(string s) {
		errorStream.WriteLine(s);
	}
} // Errors


public class FatalError: Exception {
	public FatalError(string m): base(m) {}
}

